/**
* @version:1.0
* @author kevinnan.org.cn  2018212114@mail.hfut.edu.cn
* @date 2020/7/31
* @Content:空间滤波
*/

#include "spatial_filter.hpp"

cv::Mat spatialfilter::boxFilter(cv::Mat originImage, int boxSize){
	cv::Mat tempImage;

	//定义模板
	cv::Mat mat1 = cv::Mat::ones(boxSize, boxSize, CV_8UC1);

	tempImage = relatedCalculate(mat1, originImage);

	//cv::imshow("test1", originImage);
	cv::imshow("boxFilter", tempImage);

	return tempImage;
}


cv::Mat spatialfilter::relatedCalculate(cv::Mat mat1, cv::Mat mat2){
	cv::Mat mat3;
	cv::Mat tempImage = cv::Mat(mat2.size(), 0);

	int boxSize = mat1.rows;
	int paddingSize = (boxSize-1)/2;

	//填充
	cv::copyMakeBorder(mat2, mat3, paddingSize, paddingSize, 
					   paddingSize, paddingSize, cv::BORDER_CONSTANT, cv::Scalar(0));


	//进行相关运算
	for(int i = 0; i <= mat3.rows-mat1.rows; i++){
		for(int j = 0; j <= mat3.cols-mat1.cols; j++){
			uchar value = 0;
			if(cv::sum(mat1)[0] != 0){
				value = (1.0/cv::sum(mat1)[0])*mat1.dot(mat3(cv::Rect(j, i, mat1.rows, mat1.cols)));
			}else{
				value = mat1.dot(mat3(cv::Rect(j, i, mat1.rows, mat1.cols)));
			}
			tempImage.at<uchar>(i, j) = value;
		}
	}


	return tempImage;
}


cv::Mat spatialfilter::mediaFilter(cv::Mat originImage, int boxSize){
	cv::Mat mat3;
	cv::Mat tempImage = originImage.clone();

	int paddingSize = (boxSize-1)/2;

	//填充
	cv::copyMakeBorder(originImage, mat3, paddingSize, paddingSize,
					   paddingSize, paddingSize, cv::BORDER_CONSTANT, cv::Scalar(0));


	for(int i = 0; i <= mat3.rows- boxSize; i++){
		for(int j = 0; j <= mat3.cols- boxSize; j++){
			auto roi = mat3(cv::Rect(j, i, boxSize, boxSize));
			std::vector<int> buf;
			for(int i = 0; i < roi.rows; i++){
				for(int j = 0; j < roi.cols; j++){
					buf.push_back((int)roi.ptr<uchar>(i)[j]);
				}
			}
			sort(buf.begin(), buf.end());
			tempImage.at<uchar>(i,j) = (char)buf[buf.size()/2];
		}
	}

	//cv::imshow("originImage_", originImage);
	//cv::imshow("mediaFilter", tempImage);
	
	return tempImage;
}


cv::Mat spatialfilter::laprasFilter(cv::Mat originImage, int way){
	auto originImageCopy = originImage.clone();
	originImage.convertTo(originImageCopy, CV_32SC1);
	cv::Mat tempImage;

	cv::Mat mat1 = (cv::Mat_<int>(3,3)<<0, 1, 0, 1, -4, 1, 0, 1, 0);
	cv::Mat mat2 = (cv::Mat_<int>(3,3)<<1, 1, 1, 1, -8, 1, 1, 1, 1);
	cv::Mat mat3 = (cv::Mat_<int>(3,3)<<0, -1, 0, -1, 4, -1, 0, -1, 0);
	cv::Mat mat4 = (cv::Mat_<int>(3,3)<<-1, -1, -1, -1, 8, -1, -1, -1, -1);

	cv::Mat laprasImage;

	switch(way){
		case 0:
			laprasImage = templateCalculate(mat1, originImageCopy);
			break;
		case 1:
			laprasImage = templateCalculate(mat2, originImageCopy);
			break;
		case 2:
			laprasImage = templateCalculate(mat3, originImageCopy);
			break;
		case 3:
			laprasImage = templateCalculate(mat4, originImageCopy);
			break;
		default:
			laprasImage = cv::Mat::zeros(originImageCopy.size(), 0);
			break;
	}

	//经过拉普拉斯变换后，mat数据中会产生正值和负值，但是OpenCV在显示图像时会将所有负值都置为0，因此为避免这种情况，
	//我们要将数据变换到[0,255]的区间内
	double minVal, maxVal;
	int pt_min, pt_max;
	
	cv::minMaxIdx(laprasImage, &minVal, &maxVal, nullptr, nullptr);

	cout<<"min:"<<minVal<<"\t"<<"max:"<<maxVal<<endl;

	//将图像转换到最小值为0
	cv::Mat f_m = laprasImage - minVal;

	double max_f_m;
	cv::minMaxIdx(f_m, nullptr, &max_f_m, nullptr, nullptr);

	//生成一幅标定的图像，其值在[0,255]范围内
	cv::Mat f_s = 255 * (f_m / (max_f_m+1));
	f_s /= 3;

	//将int型图像转到uchar型，便于显示
	f_s.convertTo(f_s, 0);

	tempImage = originImage - f_s;

	
	cv::imshow("test6", f_s);
	cv::imshow("test7", tempImage);
	cv::imshow("test8", originImage);

	return tempImage;
}



cv::Mat spatialfilter::templateCalculate(cv::Mat mat1, cv::Mat mat2){
	cv::Mat mat3;
	cv::Mat tempImage = cv::Mat(mat2.size(), CV_32SC1);

	int boxSize = mat1.rows;
	int paddingSize = (boxSize-1)/2;

	//填充
	cv::copyMakeBorder(mat2, mat3, paddingSize, paddingSize, 
					   paddingSize, paddingSize, cv::BORDER_CONSTANT, cv::Scalar(0));

	mat3.convertTo(mat3, CV_32SC1);

	//进行相关运算
	for(int i = 0; i <= mat3.rows-mat1.rows; i++){
		for(int j = 0; j <= mat3.cols-mat1.cols; j++){
			int value = mat1.dot(mat3(cv::Rect(j, i, mat1.rows, mat1.cols)));

			tempImage.at<int>(i, j) = value;
		}
	}
	return tempImage;
}


cv::Mat spatialfilter::unsharpMask(cv::Mat originImage, int k){
	cv::Mat tempImage;

	cv::Mat blurryImage = boxFilter(originImage, 3);
	cv::Mat maskImage = originImage - blurryImage;

	tempImage = originImage + k * maskImage;

	cv::imshow("test9", originImage);
	cv::imshow("test10", tempImage);

	return tempImage;
}


cv::Mat spatialfilter::gradientSharp(cv::Mat originImage, int way){
	cv::Mat tempImage;

	cv::Mat mat1 = (cv::Mat_<int>(3,3)<<-1, -2, -1, 0, 0, 0, 1, 2, 1);
	cv::Mat mat2 = (cv::Mat_<int>(3,3)<<-1, 0, 1, -2, 0, 2, -1, 0, 1);
	cv::Mat mat3 = (cv::Mat_<int>(2,2)<<-1, 0, 0, 1);
	cv::Mat mat4 = (cv::Mat_<int>(2,2)<<0, -1, 1, 0);

	switch(way){
		case 0:
			tempImage = relatedCalculate(mat1, originImage);
			break;
		case 1:
			tempImage = relatedCalculate(mat2, originImage);
			break;
		case 2:
			tempImage = relatedCalculate(mat3, originImage);
			break;
		case 3:
			tempImage = relatedCalculate(mat4, originImage);
			break;
		default:
			tempImage = cv::Mat::zeros(originImage.size(), 0);
			break;
	}

	cv::imshow("test11", originImage);
	
	cv::imshow("test12", mediaFilter(tempImage, 3));
	return tempImage;
}